﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WeiTap.Data.Providers;
using WeiTap.Data.Utilities;

namespace WeiTap.Data.Core
{
    public abstract class DatabaseProvider:IProvider
    {
        public abstract DbProviderFactory GetFactory();
        public virtual bool HasNativeGuidSupport => false;
        public virtual IPagingHelper PagingUtility => PagingHelper.Instance;

        public virtual string EscapeTableName(string tableName) {
            return tableName.IndexOf('.') >= 0 ? tableName : EscapeSqlIdentifier(tableName);
        }
        public virtual string EscapeSqlIdentifier(string sqlIdentifier) {
            return string.Format("[{0}]", sqlIdentifier);
        }
        public virtual string GetParameterPrefix(string connectionString) {
            return "@";
        }
        public virtual object MapParameterValue(object value) {
            if (value is bool)
                return ((bool)value) ? 1 : 0;
            return value;
        }
        public virtual Task PreExecuteAsync(DbCommand cmd) {

            return Task.FromResult(default(object));
        }
        public virtual string BuildPageQuery(long skip, long take, SQLParts parts, ref object[] args) {
            var sql = string.Format("{0}\nLIMIT @{1} OFFSET @{2}", parts.Sql, args.Length, args.Length + 1);
            args = args.Concat(new object[] { take, skip }).ToArray();
            return sql;
        }
        public virtual string GetExistsSql() {
            return "SELECT COUNT(*) FROM {0} WHERE {1}";
        }
        public virtual string GetAutoIncrementExpression(TableInfo tableInfo) {
            return null;
        }
        public virtual string GetInsertOutputClause(string primaryKeyName) {
            return string.Empty;
        }
        public virtual async Task<object> ExecuteInsertAsync(Database database, DbCommand cmd, string primaryKeyName) {
            cmd.CommandText += "; SELECT @@IDENTITY AS NewId;";
            return await database.ExecuteScalarHelperAsync(cmd);
        }
        protected DbProviderFactory GetFactory(string assemblyQualifiedName) {
            var ft = Type.GetType(assemblyQualifiedName);
            if (ft == null)
                throw new ArgumentException("未能初始化DbProviderFactory：" + assemblyQualifiedName);
            return (DbProviderFactory)ft.GetField("Instance").GetValue(null);
        }
        internal static IProvider Resolve(Type type, bool allowDefault, string connectionString) {
            var typeName = type.Name;
            if (typeName.Equals("SqlConnection") || typeName.Equals("SqlClientFactory"))
                return Singleton<SqlServerDatabaseProvider>.Instance;
            return Singleton<MySqlDatabaseProvider>.Instance;

        }
        /// <summary>
        /// 目前只支持Mysql
        /// </summary>
        /// <param name="providerName"></param>
        /// <param name="allowDefault"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        internal static IProvider Resolve(string providerName, bool allowDefault, string connectionString) {
            if (providerName.IndexOf("MySql", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<MySqlDatabaseProvider>.Instance;
            if (providerName.IndexOf("SqlServer", StringComparison.InvariantCultureIgnoreCase) >= 0 ||
                providerName.IndexOf("mssql",StringComparison.InvariantCultureIgnoreCase)>=0||
                providerName.IndexOf("System.Data.SqlClient", StringComparison.InvariantCultureIgnoreCase) >= 0)
                return Singleton<SqlServerDatabaseProvider>.Instance;
            return Singleton<MySqlDatabaseProvider>.Instance;
        }
        internal static DbProviderFactory Unwrap(DbProviderFactory factory) {
            var sp = factory as IServiceProvider;
            if (sp == null)
                return factory;
            var unwrapped = sp.GetService(factory.GetType()) as DbProviderFactory;
            return unwrapped == null ? factory : Unwrap(unwrapped);
        }
    }

    public static class Singleton<T> where T : new() {
        public static T Instance = new T();
    }
}
